﻿using System.Collections.Generic;
using UnityEngine;
using Gdmec.Cache;
namespace Gdmec.Load
{
    public class Loader : MonoBehaviour
    {
        internal CacheManager Cache;
        private readonly Queue<LoadWorker> worker_pools = new Queue<LoadWorker>();
        private readonly List<LoadWorker> workers = new List<LoadWorker>();

        private readonly Queue<LoadTask> task_pools = new Queue<LoadTask>();
        private readonly List<LoadTask> tasks = new List<LoadTask>();

        private readonly Dictionary<string, LoadTask> task_dict = new Dictionary<string, LoadTask>();

        protected virtual void Awake() 
        {
        }

        /// <summary>
        /// 获取加载任务
        /// </summary>
        /// <returns></returns>
        public LoadTask GetLoadTask() 
        {
            return TaskDequeue();
        }

        /// <summary>
        /// 执行任务
        /// </summary>
        /// <param name="task"></param>
        public void DoLoadTask(LoadTask task) 
        {
            if (task_dict.TryGetValue(task.name, out LoadTask temp_task))
            {
                temp_task.onFinish += task.onFinish;
                temp_task.onFinish_all += task.onFinish_all;
            }
            else
            {
                task_dict.Add(task.name, task);
                task.Init();
                LocalUpdate();
            }
        }

        /// <summary>
        /// 工作者
        /// </summary>
        /// <returns></returns>
        private LoadWorker WorkerDequeue()
        {
            LoadWorker worker = null;
            if (worker_pools.Count <= 0)
            {
                worker = new LoadWorker();
            }
            else 
            {
                worker = worker_pools.Dequeue();
            }
            worker.Init();
            workers.Add(worker);
            return worker;
        }

        private void WorkerEnqueue(LoadWorker worker)
        {
            workers.Remove(worker);
            worker.UnInit();
            worker_pools.Enqueue(worker);
        }

        //任务单
        private LoadTask TaskDequeue()
        {
            LoadTask task;
            if (task_pools.Count <= 0)
            {
                task = new LoadTask();
            }
            else 
            {
                task = task_pools.Dequeue();
            }
            tasks.Add(task);
            return task;
        }

        private void TaskEnqueue(LoadTask task)
        {
            task_dict.Remove(task.name);
            tasks.Remove(task);
            task_pools.Enqueue(task);
            task.Finish();
        }


        /// <summary>
        /// 任务完成
        /// </summary>
        /// <param name="task"></param>
        private void TaskFinish(LoadTask task) 
        {
            TaskEnqueue(task);
            LocalUpdate();
        }

        /// <summary>
        /// 工作者完成
        /// </summary>
        /// <param name="worker"></param>
        internal void WorkerFinish(LoadWorker worker) 
        {
            worker.task.TryFinish(worker);
            WorkerEnqueue(worker);
            LocalUpdate();
        }

        /// <summary>
        /// 是否已缓存
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public virtual bool HasCache(string path) 
        {
            return false;
        }

        /// <summary>
        /// 缓存中获取
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public virtual Object GetCache(string path) 
        {
            return Cache.Get<Object>(path);
        }

        private LoadTask temp_task;
        private LoadParam temp_param;
        private LoadWorker temp_worker;
        void LocalUpdate() 
        {
            if (tasks.Count <= 0) return;
            temp_task = tasks[0];
            if (temp_task.IsFinish)
            {
                TaskFinish(temp_task);
            }
            else 
            {
                while (temp_task.Count > 0) 
                {
                    temp_param = temp_task.GetParam;
                    if (temp_param != null)
                    {
                        if (HasCache(temp_param.path))
                        {
                            temp_task.TryFinish(temp_param);
                        }
                        else
                        {
                            temp_worker = WorkerDequeue();
                            temp_worker.task = temp_task;
                            temp_worker.param = temp_param;
                            temp_worker.coroutine = StartCoroutine(temp_worker.Load(this, temp_worker));
                        }
                    }
                }
            }
            temp_param = null;
            temp_task = null;
        }

        void Update()
        {
            LocalUpdate();
        }
    }
}